﻿using HalconDotNet;
using NModbus;
using NModbus.Device;
using NModbus.Serial;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;

namespace WindowsFormsApp2.接口类
{
    public class COM_ModbusRTU
    {
        public SerialPort serialPort;
        private IModbusSerialMaster master;
        public Boolean portOpen = false;
        private int slavecount = 0;
        /// <summary>
        /// 初始化串口参数，串口号需填入，其他存在默认值
        /// </summary>
        /// <param name="portName"></param>
        /// <param name="baudRate"></param>
        /// <param name="parity"></param>
        /// <param name="dataBits"></param>
        /// <param name="stopBits"></param>
        public COM_ModbusRTU(string portName = "COM1", int baudRate = 9600, Parity parity = Parity.None, int dataBits = 8, StopBits stopBits = StopBits.One)
        {
            serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
        }

        /// <summary>
        /// 打开串口并创建 Modbus 主站
        /// </summary>
        /// <returns></returns>
        public bool Open()
        {
            try
            {
                var factory = new ModbusFactory();

                //需要先创建一个基于串口的 库中主站类型对象
                master = factory.CreateRtuMaster(serialPort);

                // 设置串口读写超时时间，单位为毫秒
                serialPort.ReadTimeout = 1000;
                serialPort.WriteTimeout = 10;
                // 打开串口
                if (!serialPort.IsOpen)
                    serialPort.Open();



                portOpen = true;
                return true;
            }
            catch (Exception ex)
            {
                System.Windows.Forms.MessageBox.Show(
                                 $"串口打开失败: {ex.Message}",
                                 " ",
                                 System.Windows.Forms.MessageBoxButtons.OK,
                                 System.Windows.Forms.MessageBoxIcon.Information,
                                 System.Windows.Forms.MessageBoxDefaultButton.Button1,
                                 System.Windows.Forms.MessageBoxOptions.DefaultDesktopOnly |
                                 System.Windows.Forms.MessageBoxOptions.ServiceNotification);
                

                return false;
            }
        }
        /// <summary>
        /// 读取指定站号的指定保持寄存器内容
        /// （相较于按协议发送字节，省去了计算校验的麻烦）
        /// （同时，其返回的是每个寄存器数据转化后的16位int(2字节)，如果需要多个寄存器位置组合转化数据，需要自己处理）
        /// </summary>
        /// <param name="slaveAddress"></param>
        /// <param name="startAddress"></param>
        /// <param name="numRegisters"></param>
        /// <returns></returns>
        public ushort[] ReadRegisters(byte slaveAddress, ushort startAddress, ushort numRegisters)
        {
            try
            {
                return master.ReadHoldingRegisters(slaveAddress, startAddress, numRegisters);
            }
            catch (Exception ex)
            {
                Debug.WriteLine($"读取失败：{ex.Message}");
                return null;
            }
        }
        /// <summary>
        /// 读取寄存器，自定义硬超时
        /// </summary>
        /// <param name="slaveAddress"></param>
        /// <param name="startAddr"></param>
        /// <param name="numRegisters"></param>
        /// <param name="totalTimeout"></param>
        /// <returns></returns>
        public ushort[] ReadRegistersWithHardTimeout(byte slaveAddress, ushort startAddr, ushort numRegisters, int totalTimeout = 50)
        {
            var cts = new CancellationTokenSource(totalTimeout);
            try
            {
                return Task.Run(() =>
                    master.ReadHoldingRegisters(slaveAddress, startAddr, numRegisters),
                    cts.Token
                ).Result;
            }
            catch (AggregateException ex) when (ex.InnerException is TimeoutException)
            {
                return null; // 硬超时触发
            }
        }
        /// <summary>
        /// 读取寄存器为int32
        /// </summary>
        /// <param name="twoByteNumHigh"></param>
        /// <param name="twoByteNumLow"></param>
        /// <returns></returns>
        public int ConvertToInt(ushort twoByteNumHigh, ushort twoByteNumLow)
        {

            try
            {
                //高位ushort左移16位，后面空的16位与低位求或
                //解析寄存器字节时，最好直接使用位移操作，直接操作字节，高效
                return ((int)twoByteNumHigh << 16) | twoByteNumLow;
            }
            catch (Exception ex)
            {

                return 0;
            }
        }
        /// <summary>
        /// 将2个单独寄存器读出的ushort转化为uint
        /// </summary>
        /// <param name="twoByteNumHigh"></param>
        /// <param name="twoByteNumLow"></param>
        /// <returns></returns>
        public uint ConvertToUint(ushort twoByteNumHigh, ushort twoByteNumLow)
        {

            try
            {
                //高位ushort左移16位，后面空的16位与低位求或
                return ((uint)twoByteNumHigh << 16) | twoByteNumLow;
            }
            catch (Exception ex)
            {

                return 0;
            }
        }

        /// <summary>
        /// 获取已连接从站数量
        /// </summary>
        /// <returns></returns>
        public int GetSlaveCount()
        {
            int slaveCount = 0;
            // 轮询从站地址 1 - 200
            for (byte slaveId = 1; slaveId <= 7; slaveId++)
            {
                Stopwatch sw = Stopwatch.StartNew();
                try
                {
                    // 尝试读取从站的保持寄存器（可以根据实际情况修改功能码和寄存器地址）
                    sw.Start();
                    ushort[] registers = ReadRegistersWithHardTimeout(slaveId, 0, 1);
                    // 如果没有抛出异常，说明从站响应正常
                    slaveCount++;
                    Console.WriteLine($"从站 ID {slaveId} 已连接。");
                }
                catch (TimeoutException)
                {
                    double time = sw.ElapsedMilliseconds;
                    sw.Stop();
                    // 捕获超时异常，说明该从站可能未连接或响应超时
                    Console.WriteLine($"从站 ID {slaveId} 未连接或响应超时:{time}ms。");
                }
                catch (Exception ex)
                {
                    // 捕获异常，说明该从站可能未连接

                    Console.WriteLine($"从站 ID {slaveId} 发生错误: {ex.Message}");
                }
                Thread.Sleep(1);
            }
            slavecount = slaveCount;
            return slaveCount;



        }

        /// <summary>
        /// 读取suoyou从站设备的某一数据(两个连续寄存器),从1号shebei开始读取
        /// </summary>
        /// <param name="registerStart"></param>
        /// <returns></returns>
        public int[] ReadAllSlaveData(ushort slavecount, ushort registerStart = 0)
        {
            if (!serialPort.IsOpen)
            {
                Console.WriteLine("串口未打开，请先调用 Open 方法。");
                return null;
            }
            int[] ints = new int[slavecount];
            for (byte slaveId = 1; slaveId <= slavecount; slaveId++)
            {
                try
                {
                    ushort[] registers = master.ReadHoldingRegisters(slaveId, registerStart, 2);
                    int data = ConvertToInt(registers[0], registers[1]);//默认高、低
                    ints[slaveId - 1] = data;
                    Console.WriteLine($"从站 ID: {slaveId}");
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"读取从站 ID {slaveId} 数据时出错: {ex.Message}");
                }
                Thread.Sleep(5);
            }
            return ints;
        }
        /// <summary>
        /// 读取指定ID从站设备的某一数据(两个连续寄存器)
        /// </summary>
        /// <param name="slaveid"></param>
        /// <param name="registerStart"></param>
        /// <returns></returns>
        public int ReadSingleSlaveData(int slaveid, ushort registerStart = 0)
        {
            if (!serialPort.IsOpen)
            {
                Console.WriteLine("串口未打开，请先调用 Open 方法。");
                return 0;
            }
            int ints = 0;
            byte slaveId = (byte)slaveid;
            try
            {

                ushort[] registers = master.ReadHoldingRegisters(slaveId, registerStart, 2);
                int data = ConvertToInt(registers[0], registers[1]);//默认高、低
                ints = data;
                Console.WriteLine($"从站 ID: {slaveId}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"读取从站 ID {slaveId} 数据时出错: {ex.Message}");
            }


            return ints;
        }
        public async Task<int[]> ReadSlaveDataParallel(ushort slavecount, ushort registerStart = 0)
        {
            if (!serialPort.IsOpen)
            {
                Console.WriteLine("串口未打开，请先调用 Open 方法。");
                return null;
            }
            var tasks = new List<Task<ushort[]>>();
            int[] ints = new int[slavecount];
            // 并行发起所有请求
            //让6个task轮流开始运行
            for (byte addr = 1; addr <= slavecount; addr++)
            {
                tasks.Add(Task<ushort[]>.Run(() => (master.ReadHoldingRegisters(addr, 0, 2))));
            }

            // 等待所有响应（最大等待50ms）
            ushort[][] results = await Task.WhenAll(tasks);
            for (int i = 0; i <= slavecount; i++)
            {
                ushort high = results[i][0];
                ushort low = results[i][1];
                ints[i] = ConvertToInt(high, low);
            }



            return ints;
        }
        // 关闭串口
        public void Close()
        {
            if (serialPort.IsOpen)
            {
                serialPort.Close();
                portOpen = false;
            }
        }





    }
}
